package org.zoomdev.zoom.dao.adapters.result;

import org.zoomdev.zoom.common.caster.TypeKey;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.dao.adapters.ResultSetAdapter;
import org.zoomdev.zoom.dao.adapters.parameter.ParameterAdapterRegistry;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Clob;
import java.sql.Types;
import java.time.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class ResultSetAdapters {

    private static final Map<TypeKey, ResultSetAdapter> pool = new HashMap<>();
    private static final HashMap<Class<?>, ResultSetAdapter> rawMap = new HashMap<>();

    static {
        registerRaw(int.class, new IntResultSetAdapter());
        registerRaw(Integer.class, new IntResultSetAdapter());
        registerRaw(long.class, new LongResultSetAdapter());
        registerRaw(Long.class, new LongResultSetAdapter());
        registerRaw(float.class, new FloatResultSetAdapter());
        registerRaw(Float.class, new FloatResultSetAdapter());
        registerRaw(double.class, new DoubleResultSetAdapter());
        registerRaw(Double.class, new DoubleResultSetAdapter());
        registerRaw(short.class, new ShortResultSetAdapter());
        registerRaw(Short.class, new ShortResultSetAdapter());
        registerRaw(byte.class, new ByteResultSetAdapter());
        registerRaw(Byte.class, new ByteResultSetAdapter());
        registerRaw(boolean.class, new BooleanResultSetAdapter());
        registerRaw(Boolean.class, new BooleanResultSetAdapter());
        registerRaw(String.class, new StringResultSetAdapter());
        registerRaw(java.sql.Date.class, new SqlDateResultSetAdapter());
        registerRaw(java.sql.Time.class, new SqlTimeResultSetAdapter());
        registerRaw(java.sql.Timestamp.class, new SqlTimestampResultSetAdapter());
        registerRaw(LocalDate.class, ObjectResultSetAdapter.ADAPTER);
        registerRaw(LocalTime.class, ObjectResultSetAdapter.ADAPTER);
        registerRaw(LocalDateTime.class, ObjectResultSetAdapter.ADAPTER);
        registerRaw(BigDecimal.class, new BigDecimalResultSetAdapter());
        registerRaw(BigInteger.class, ObjectResultSetAdapter.ADAPTER);
        registerRaw(byte[].class, new BytesResultSetAdapter());

        registerPair(java.sql.Date.class, LocalDate.class, new Date2LocalDateResultSetAdapter());
        registerPair(java.sql.Time.class, LocalTime.class, new Time2LocalTimeResultSetAdapter());
        registerPair(java.sql.Timestamp.class, Instant.class, new Timestamp2InstantResultSetAdapter());
        registerPair(java.sql.Date.class, Year.class, new Date2YearResultSetAdapter());
    }

    public static <T> void registerRaw(Class<T> columnType, ResultSetAdapter adapter) {
        rawMap.put(columnType, adapter);
    }

    public static <T> void registerPair(Class<?> columnType, Class<T> fieldType, ResultSetAdapter<T> adapter) {
        pool.put(TypeKey.of(columnType, fieldType), adapter);
    }

    /**
     * 获取的数据直接往field方向靠拢
     *
     * @param columnType
     * @param fieldType
     * @return
     */
    public static ResultSetAdapter create(Class<?> columnType, Type fieldType) {
        if (columnType == fieldType) {
            return rawMap.get(columnType);
        }
        TypeKey key = TypeKey.of(columnType, fieldType);
        ResultSetAdapter adapter = pool.get(key);
        if (adapter == null) {
            adapter = create2(columnType, fieldType);
            pool.putIfAbsent(key, adapter);
        }
        return adapter;
    }

    public static ResultSetAdapter create(int sqlType) {
        if (sqlType == Types.BLOB) {
            return BytesResultSetAdapter.ADAPTER;
        }
        if (sqlType == Types.CLOB) {
            return StringResultSetAdapter.ADAPTER;
        }
        return ObjectResultSetAdapter.ADAPTER;
    }

    public static ResultSetAdapter create(Class<?> columnType) {
        if (columnType == byte[].class) {
            return BytesResultSetAdapter.ADAPTER;
        }
        if (columnType == String.class) {
            return StringResultSetAdapter.ADAPTER;
        }
        return ObjectResultSetAdapter.ADAPTER;
    }

    protected static ResultSetAdapter create2(Class<?> columnType, Type fieldGenericType) {
        //enum
        if (fieldGenericType instanceof Class) {
            Class<?> fieldType = (Class<?>) fieldGenericType;
            if (Classes.isEnum(fieldType)) {
                if (columnType == String.class) {
                    return new StringEnumResultSetAdapter(fieldType);
                }
            }

            if (ParameterAdapterRegistry.isPrimitive(fieldType)) {
                if (columnType == Integer.class) {
                    return rawMap.get(fieldType);
                }
                if (columnType == String.class) {
                    return rawMap.get(String.class);
                }
            }


        }

        if (fieldGenericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) fieldGenericType;
            if (pt.getRawType() instanceof TypeVariable) {
                throw new ZoomException("不支持的类型:" + fieldGenericType);
            }
            Class<?> rawType = (Class<?>) pt.getRawType();
            if (Map.class.isAssignableFrom(rawType) || Collection.class.isAssignableFrom(rawType)) {
                if (columnType == Clob.class) {
                    return new ClobJsonResultSetAdapter(fieldGenericType);
                }
                //需要做中转
                return new StringJsonResultSetAdapter(fieldGenericType);
            }
        }

        return new WrapTypeResultSetAdapter(columnType, fieldGenericType);

    }
}
